//TP_noté de COMPILATION
//Auteurs : Marie DUVAUDIER, Carl-Johan JORGENSEN
//Premiére version

tree grammar vslTree;

options {
    	tokenVocab=vsl;
    	ASTLabelType=CommonTree;
	//pour commencer nous avons suppimer des options : "output=AST", en effet ce n'est plus un AST qui est rendu en sortie.
}

//@header {}


// nous déclarons une tables des symboles pour pouvoir l'utiliser ainsi qu'une chaine qui dira si nous sommes en train de déclarer une fonction ou un prototype.
@members	{
			TableSymboles tabSymboles = new TableSymboles();
			boolean Fonction = false;
		}



/*------------------------------------------------------------------
 * TREE RULES
 *------------------------------------------------------------------*/

program returns [Code c]
		@init{c=new Code();}
		: 	^(PROG (u=unite 
			{	
				if(u == null)
				{
					System.err.println("On ne trouve pas d'unité dans le programme, il en faut au moin une"); 
					System.exit(1); 
				}
				else
				{
				c.append(u); 
				tabSymboles.Dump(); 
				}
			}
			)+)
		;

unite returns [Code c]
		@init{c = new Code();}
		:	^(FUNC_KW t1=type id1=IDENT par1=param_list ^(CORPS s=statement))
			{
				Fonction = true; 
				Code cod;
				Tokatt tok;
				Tokatt label;
				Inst3a in;
				cod = new Code();
				tok = tabSymboles.Lookup(id1.getText());
				label = new Tokatt(id1.getText(), t1);

				if(tok !=null && tok.scope !=0)
				{	
					System.err.println("variable déja existante");
					System.exit(1); 
				}
				else if(tok !=null && tok.scope == 0)
				{	
					in = new Inst3a(Inst3a.TAC_LABEL, label, null, null);
					cod.append(new Code(in));
					in = new Inst3a(Inst3a.TAC_BEGINFUNC,null,null,null);
					cod.append(new Code(in));
					cod.append(par1);
					cod.append(s);
					in = new Inst3a(Inst3a.TAC_ENDFUNC,null,null,null);
					cod.append(new Code(in));
				}
				else
				{	
					tok = new Tokatt(t1, id1.getText(), 0);
					tabSymboles.Insert(id1.getText(), tok);
					in = new Inst3a(Inst3a.TAC_LABEL, label, null, null);
					cod.append(new Code(in));
					in = new Inst3a(Inst3a.TAC_BEGINFUNC,null,null,null);
					cod.append(new Code(in));
					cod.append(par1);
					cod.append(s);
					in = new Inst3a(Inst3a.TAC_ENDFUNC,null,null,null);
					cod.append(new Code(in));
				}
			c = cod;				
			}
		|	^(PROTO_KW t2=type id2=IDENT p2=param_list)
			{
				Fonction = false;
				Code cod;
				Tokatt tok;
				
				cod = new Code();
				tok = tabSymboles.Lookup(id2.getText());

				if(tok !=null && tok.scope !=0)
				{
					System.err.println("variable déja existante");
					System.exit(1); 
				}
				else if(tok !=null && tok.scope == 0)
				{
					return c;
				}
				else
				{
					tok = new Tokatt(t2, id2.getText(), 0);
					tabSymboles.Insert(id2.getText(), tok);
				}
			c = cod;				
			}
		;

type returns [Type t]
		@init{t = null;}
		:	INT_KW
			{
				t = TypeSystem.T_integer;
			}
		|	VOID_KW 
			{
				t = TypeSystem.T_void;
			}
		;

param_list returns [Code c]
		@init{c = new Code();}
		:	^(PARAM p=param*)
			{	
				if(p==null)
				{
					return c;
				}
				else
				{
					c.append(p);
				}
			}
		;

param returns [Code c]
		@init{c = new Code();}
		:	id=IDENT
			{
				Code cod;
				Inst3a in;
				Tokatt tok;
				Tokatt param;
			
				cod = new Code();
				tok = tabSymboles.Lookup(id.getText());
				
				if (Fonction == true)
				{
					if(tok!=null)
					{
						System.err.println("variable déja existante");
						System.exit(1);
					}
					else
					{
						param = new Tokatt(TypeSystem.T_integer, id.getText(), 0);
						tabSymboles.Insert(id.getText(), param);
						in = new Inst3a(Inst3a.TAC_VAR, param, null, null);
						cod.append(new Code(in));
					}
				}
				else 
				{
					if(tok!=null)
					{
						System.err.println("variable déja existante");
						System.exit(1);
					}
					else
					{
						param = new Tokatt(TypeSystem.T_integer, id.getText(), 0);
						tabSymboles.Insert(id.getText(), param);
					}									
				}
				c = cod;
			}
	        |	^(ARRAY id=IDENT) 
			{
				Code cod;
				Inst3a in;			
				Tokatt tok;
				Tokatt param;
				String iden = id.getText();
				TypeArray typeTab = new TypeArray(TypeSystem.T_integer, 0);
				
				tok = tabSymboles.Lookup(iden);
				cod = new Code();

				if (Fonction == true)
				{
					if(tok!=null)
					{
						System.err.println("tableau deja existant");
						System.exit(1);
					}
					else
					{

						param = new Tokatt(typeTab,iden,0);
						tabSymboles.Insert(iden, tok);
						in = new Inst3a(Inst3a.TAC_VAR, param, null, null);
						cod.append(new Code(in));
					}
				}
				else 
				{
					if(tok!=null)
					{
						System.err.println("tableau déja existant");
						System.exit(1);
					}
					else
					{				
						param = new Tokatt(typeTab,iden,0);
						tabSymboles.Insert(id.getText(), param);
					}	
				}								
				c = cod;
			}


		;

statement returns [Code c]
		@init{c = new Code();}
		:	^(ASSIGN_KW id=IDENT exp=expression)
			{	
				String nameIdent;
				Tokatt rechercheIdent;
				Inst3a i;
				Tokatt temp = SymbDistrib.newTemp();

				nameIdent = id.getText();
				rechercheIdent = tabSymboles.Lookup(nameIdent);

				if(rechercheIdent == null || rechercheIdent.scope != tabSymboles.getScope())
				{
					System.err.println("Ident pas déclaré");
					System.exit(1);
				}
				else
				{	
					i = new Inst3a(Inst3a.TAC_VAR, temp, null, null);
					c.append(new Code(i));	
					i = new Inst3a(Inst3a.TAC_COPY, temp, exp.place, null);
					c.append(new Code(i));
					i = new Inst3a(Inst3a.TAC_COPY, rechercheIdent, temp, null);
					c.append(new Code(i));
				}
			}
		|	^(ASSIGN_KW tab=tab_elem exp=expression)
			{
				Inst3a in;

				if(exp==null)
				{
					System.err.println("il n'y a pas d'expression a assigner");
					System.exit(1);
				}
				else
				{
				c.append(exp.code);
				in = new Inst3a(Inst3a.TAC_VARTAB, tab.ident , tab.expression , exp.place);
				c.append(new Code(in));
				}
				
			}	
		|	^(RETURN_KW exp=expression)
			{
				Inst3a in;
				
				in = new Inst3a(Inst3a.TAC_RETURN, exp.place, null, null);
				c.append(new Code(in));
			}
		|	^(PRINT_KW p=print_list)
			{
				c=p;
			}
		|	^(READ_KW r=read_list)
			{
				c=r;
			}
		|	^(IF_KW exp1=expression s1=statement (s2=statement)?)
			{				
				Type ty;
				ty = TypeSystem.CheckInt(exp1.type);
				if(ty == TypeSystem.T_integer)

				{
					c = Code.genIf(exp1, s1, s2);
				}
				else 
				{
					System.err.println("Probléme de type dans le traitement de statement"); 
					System.exit(1);
				}
			}
		|	^(WHILE_KW expr=expression s=statement)
			{	
				Type ty;

				ty = TypeSystem.CheckInt(expr.type);
				if(ty == TypeSystem.T_integer)
				{
					c = Code.genWhile(expr, s);
				}
				else 
				{
					System.err.println("Probléme de type dans le traitement de statement"); 
					System.exit(1);
				}
			}
			
		|	^(FCALL_S id=IDENT arg=argument_list)
			{	
				Code cod;
				Inst3a in;
				Tokatt tok;
			
				cod = new Code();

				if(tabSymboles.Lookup(id.getText())==null)
				{
					System.err.println("La fonction n'existe pas");
					System.exit(1);
				}
				else
				{
					tok = new Tokatt(id.getText());
					cod = new Code();

					in = new Inst3a(Inst3a.TAC_CALL, tok, null, null);
					cod.append(new Code(in));
					cod.append(arg.code);
				}
			c = cod;
			}
		|	b=block 
			{ c = b;}
		;

block returns [Code c]
		@init{c=new Code();}
		:	^(BLOCK dec=declaration (s=suit_inst)+)
			{
				tabSymboles.Enter_Scope();				
				c = Code.genBlock(dec,s);
				tabSymboles.Leave_Scope();
			}
		|	^(BLOCK (s=suit_inst {c.append(s);})+) 
		;


tab_elem returns [TableAtt t]
		@init{t=null;}
		: 	^(ARELEM id=IDENT exp=expression)
			{
				Inst3a in;
				Tokatt tok;
				Code c;
				Tokatt ident;
				Expratt expr;

				tok = SymbDistrib.newTemp();
				c = new Code();
				ident = tabSymboles.Lookup(id.getText());

	
				if(ident==null && ident.scope != tabSymboles.getScope())
	
				{
					System.err.println("tableau pas encore déclaré");
					System.exit(1);
				}
				else
				{	
					in = new Inst3a(Inst3a.TAC_VAR, tok, null, null);
					c.append(new Code(in));
					in = new Inst3a(Inst3a.TAC_TABVAR, tok, tabSymboles.Lookup(id.getText()), exp.place);
					c.append(new Code(in));
				}
				expr = new Expratt(TypeSystem.T_integer, c, tok);
				t = new TableAtt(ident,exp.place, expr);
			}			
		;

suit_inst returns [Code c]
		@init{c = new Code();}
		:	^(INST ( s = statement { c.append(s); } )+)
		;


expression returns [Expratt exp]
		@init{exp = null;}
		:	^(PLUS exp1=expression  exp2=expression)
			{Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
				  Tokatt temp = SymbDistrib.newTemp();
				  Code cod = Code.genBinOp(Inst3a.TAC_ADD, exp1, exp2, temp);
				  exp = new Expratt(ty, cod, temp);
}
		|	^(MOINS exp1=expression exp2=expression)
			{Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
				  Tokatt temp = SymbDistrib.newTemp();
				  Code cod = Code.genBinOp(Inst3a.TAC_SUB, exp1, exp2, temp);
				  exp = new Expratt(ty, cod, temp);
}
		|	^(MUL exp1=expression  exp2=expression)
			{Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
				  Tokatt temp = SymbDistrib.newTemp();
				  Code cod = Code.genBinOp(Inst3a.TAC_MUL, exp1, exp2, temp);
				  exp = new Expratt(ty, cod, temp);
}
		|	^(DIV exp1=expression  exp2=expression)
			{Type ty = TypeSystem.CheckBinOp(exp1.type, exp2.type);
				  Tokatt temp = SymbDistrib.newTemp();
				  Code cod = Code.genBinOp(Inst3a.TAC_DIV, exp1, exp2, temp);
				  exp = new Expratt(ty, cod, temp);
}
		|	e = primaire {exp=e;}
		;



primaire  returns [Expratt exp]
		@init{exp = null;}
		:	i=INTEGER
			{int ival =Integer.parseInt(i.getText());
				 exp = new Expratt(TypeSystem.T_integer, new Code(), new Tokatt(ival));}
		|	id=IDENT
			{
				Tokatt tok = tabSymboles.Lookup(id.getText());
				if(tok==null && tok.scope == tabSymboles.getScope())
				{
					System.err.println("la variable n'existe pas");
					System.exit(1);
				}
				else
				{
					Code c = new Code();
					Tokatt temp = SymbDistrib.newTemp();
					exp = new Expratt(tok.type,c,tok);
				}
			}
      		|	tab=tab_elem
			{
				Tokatt tok = tabSymboles.Lookup(tab.ident.name);
				if(tok==null && tok.scope == tabSymboles.getScope())
				{
					System.err.println("le tableau n'existe pas");
					System.exit(1);
				}
				else
				{
					Code c = new Code();
					exp = new Expratt(tok.type,c,tab.ident);
				}
			}
		|	^(FCALL id=IDENT arg=argument_list)
			{       
				Tokatt tok = tabSymboles.Lookup(id.getText());
				Tokatt tok2;
				Inst3a in;
				
				if(tok==null)
				{
					System.err.println("la fonction n'existe pas");
					System.exit(1);
				}
				else
				{
					Code c = new Code();
					tok2 = new Tokatt(id.getText());
					Tokatt temp = SymbDistrib.newTemp();

					in = new Inst3a(Inst3a.TAC_VAR, temp, null, null);
					c.append(new Code(in));
					in = new Inst3a(Inst3a.TAC_ARG, arg.place, null, null);
					c.append(new Code(in));
					in = new Inst3a(Inst3a.TAC_CALL, null, tok, null);
					c.append(new Code(in));
					in = new Inst3a(Inst3a.TAC_COPY, temp, tok, null);
					c.append(new Code(in));
					exp = new Expratt(tok.type,c,temp);
				}
			}
				

		;

argument_list returns [Expratt e]
		@init{e=null;}
		:	(exp = expression
			{Code c = Code.genArgList(exp);e = new Expratt(exp.type, c, exp.place);})+
		;

print_list	returns [Code c]
		@init{c=new Code();}
		:	p1=print_item (p2=print_item)*
			{
					c=Code.genPrintList(p1,p2);
			}
		;

print_item returns [Code c]
		@init{c= new Code();}
		:	t=TEXT
			{	
				c=Code.genPrintText(t.getText());
			}
		|	exp=expression
			{
				c=Code.genPrintExpr(exp);
			}
		;

read_list returns [Code c]
		@init{c = new Code();}
		:	(r=read_item {c.append(r);})+
		;

read_item returns [Code c]
		@init{c = new Code();}
		:	id=IDENT
			{
				Tokatt L8;
				Inst3a i;
				Tokatt t2;

				t2 = tabSymboles.Lookup(id.getText());		
				L8 = new Tokatt("L8",null);

			if(t2==null && t2.scope != tabSymboles.getScope())
			{
				System.err.println("la variable n'existe pas");
				System.exit(1);
			}
			else
			{
				i = new Inst3a(Inst3a.TAC_CALL, t2, L8, null);
				c.append(new Code(i));
			}
		}
		|	tab=tab_elem
			{
				Tokatt L8;
				Inst3a i;
				Tokatt t2;

				t2 = tab.ident;		
				L8 = new Tokatt("L8",null);

			if(t2==null && t2.scope != tabSymboles.getScope())
			{
				System.err.println("la variable n'existe pas");
				System.exit(1);
			}
			else
			{
				i = new Inst3a(Inst3a.TAC_CALL, t2, L8, null);
				c.append(new Code(i));
			}
		}
		;

declaration returns [Code c]
		@init {c = new Code();}
		:	^(DEC (d=dec_item{ c.append(d); })+) 
			
		;

dec_item returns [Code c]
		@init {c = new Code();}
		:	id=IDENT
		{
			Code cod;
			Inst3a in;
			Tokatt tok;
			
			cod = new Code();

			if(tabSymboles.Lookup(id.getText())!=null && tabSymboles.Lookup(id.getText()).scope == tabSymboles.getScope())
			{
				System.err.println("variable déja existante");
				System.exit(1);
			}
			else
			{
				tok = new Tokatt(TypeSystem.T_integer,id.getText(),tabSymboles.getScope());
				tabSymboles.Insert(id.getText(), tok);
				in = new Inst3a(Inst3a.TAC_VAR, tok, null, null);
				cod.append(new Code(in));
			}
		c = cod;
		}
          	|	^(ARDEC id=IDENT it=INTEGER)
			{
			Inst3a in;			
			Tokatt tok;
			int inte = Integer.parseInt(it.getText());
			String iden = id.getText();
			TypeArray typeTab = new TypeArray(TypeSystem.T_integer, inte);

			if(tabSymboles.Lookup(iden)!=null && tabSymboles.Lookup(iden).scope == tabSymboles.getScope())

			{
				System.err.println("tableau deja existant");
				System.exit(1);
			}
			else
			{
				tok = new Tokatt(typeTab,iden,tabSymboles.getScope());
				tabSymboles.Insert(iden, tok);
				in = new Inst3a(Inst3a.TAC_VAR, tok, null, null);
				c.append(new Code(in));
			}
		}
		;


